// Raw MIDI test program

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <signal.h>
#include "asoundlib.h"

static void usage(void)
{
	fprintf(stderr, "usage: rawmidi [options]\n");
	fprintf(stderr, " options:\n");
	fprintf(stderr, " -v: verbose mode\n");
	fprintf(stderr, " -i device-id : test ALSA input device\n");
	fprintf(stderr, " -o device-id : test ALSA output device\n");
	fprintf(stderr, " -I node : test input node\n");
	fprintf(stderr, " -O node : test output node\n");
	fprintf(stderr, " -t: test midi thru\n");
	fprintf(stderr, " example:\n");
	fprintf(stderr, " rawmidi -i hw:0,0 -O /dev/midi1\n");
	fprintf(stderr, " tests input for card 0, device 0, using snd_rawmidi API\n");
	fprintf(stderr, " and /dev/midi1 using file descriptors\n");
}

int stop = 0;

void sighandler(int dum)
{
	stop = 1;
}

int main(int argc,char** argv)
{
	int i;
	int err;
	int thru = 0;
	int verbose = 0;
	char *device_in = NULL;
	char *device_out = NULL;
	char *node_in = NULL;
	char *node_out = NULL;
	int fd_in = -1,fd_out = -1;
	snd_rawmidi_t *handle_in = 0,*handle_out = 0;

	if (argc == 1)
	{
		usage();
		exit(0);
	}

	for (i = 1; i < argc; i++)
	{
		if (argv[i][0] == '-')
		{
			switch (argv[i][1])
			{
			case 'h':
				usage();
				break;

			case 'v':
				verbose = 1;
				break;

			case 't':
				thru = 1;
				break;

			case 'i':
				if (i + 1 < argc)
					device_in = argv[++i];
				break;

			case 'I':
				if (i + 1 < argc)
					node_in = argv[++i];
				break;

			case 'o':
				if (i + 1 < argc)
					device_out = argv[++i];
				break;

			case 'O':
				if (i + 1 < argc)
					node_out = argv[++i];
				break;
			}
		}
	}

	if (verbose)
	{
		fprintf(stderr, "Using: \n");
		fprintf(stderr, "Input: ");
		if (device_in)
		{
			fprintf(stderr, "device %s\n", device_in);
		}
		else if (node_in)
		{
			fprintf(stderr, "%s\n", node_in);
		}
		else
		{
			fprintf(stderr, "NONE\n");
		}
		fprintf(stderr, "Output: ");

		if (device_out)
		{
			fprintf(stderr, "device %s\n", device_out);
		}
		else if (node_out)
		{
			fprintf(stderr, "%s\n", node_out);
		}
		else
		{
			fprintf(stderr, "NONE\n");
		}
	}

	if (device_in)
	{
		err = snd_rawmidi_open(&handle_in, NULL, device_in, 0);
		if (err)
		{
			fprintf(stderr, "snd_rawmidi_open %s failed: %d\n", device_in,err);
		}
	}

	if (node_in && (!node_out || strcmp(node_out, node_in)))
	{
		fd_in = open(node_in, O_RDONLY);
		if (fd_in < 0)
		{
			fprintf(stderr, "open %s for input failed\n", node_in);
		}
	}

	signal(SIGINT, sighandler);
	if (device_out)
	{
		err = snd_rawmidi_open(NULL, &handle_out, device_out, 0);
		if (err)
		{
			fprintf(stderr,"snd_rawmidi_open %s failed: %d\n", device_out, err);
		}
	}

	if (node_out && (!node_in || strcmp(node_out, node_in)))
	{
		fd_out = open(node_out, O_WRONLY);
		if (fd_out < 0)
		{
			fprintf(stderr, "open %s for output failed\n", node_out);
		}
	}

	if (node_in && node_out && strcmp(node_out, node_in)==0)
	{
		fd_in = fd_out = open(node_out, O_RDWR);
		if (fd_out < 0)
		{
			fprintf(stderr, "open %s for input and output failed\n", node_out);
		}
	}

	if (!thru)
	{
		if (handle_in || fd_in != -1)
		{
			fprintf(stderr, "Read midi in\n");
			fprintf(stderr, "Press ctrl-c to stop\n");
		}

		if (handle_in)
		{
			unsigned char ch;
			while (!stop)
			{
				snd_rawmidi_read(handle_in, &ch, 1);
				if (verbose)
				{
					fprintf(stderr, "read %02x\n", ch);
				}
			}
		}

		if (fd_in != -1)
		{
			unsigned char ch;
			while (!stop)
			{
				read(fd_in, &ch, 1);
				if (verbose)
				{
					fprintf(stderr, "read %02x\n", ch);
				}
			}
		}

		if (handle_out || fd_out != -1)
		{
			fprintf(stderr, "Writing note on / note off\n");
		}

		if (handle_out)
		{
			unsigned char ch;
			ch = 0x90; snd_rawmidi_write(handle_out, &ch, 1);
			ch = 60; 	 snd_rawmidi_write(handle_out, &ch, 1);
			ch = 100;  snd_rawmidi_write(handle_out, &ch, 1);
			sleep(1);
			snd_rawmidi_drain(handle_out);
			ch = 0x90; snd_rawmidi_write(handle_out, &ch, 1);
			ch = 60; 	 snd_rawmidi_write(handle_out, &ch, 1);
			ch = 0; 	 snd_rawmidi_write(handle_out, &ch, 1);
			snd_rawmidi_drain(handle_out);
		}

		if (fd_out != -1)
		{
			unsigned char ch;
			ch = 0x90; write(fd_out, &ch, 1);
			ch = 60;   write(fd_out, &ch, 1);
			ch = 100;  write(fd_out, &ch, 1);
			sleep(1);
			ch = 0x90; write(fd_out, &ch, 1);
			ch = 60;   write(fd_out, &ch, 1);
			ch = 0;    write(fd_out, &ch, 1);
		}
	}
	else
	{
		if ((handle_in || fd_in!=-1) && (handle_out || fd_out!=-1))
		{
			if (verbose)
			{
				fprintf(stderr,"Testing midi thru in\n");
			}

			while (!stop)
			{
				unsigned char ch;

				if (handle_in)
				{
					snd_rawmidi_read(handle_in, &ch, 1);
				}

				if (fd_in != -1)
				{
					read(fd_in, &ch, 1);
				}

				if (verbose)
				{
					fprintf(stderr, "thru: %02x\n", ch);
				}

				if (handle_out)
				{
					snd_rawmidi_write(handle_out, &ch, 1);
					snd_rawmidi_drain(handle_out);
				}

				if (fd_out != -1)
				{
					write(fd_out, &ch, 1);
				}
			}
		}
		else
		{
			fprintf(stderr, "Testing midi thru needs both input and output\n");
			exit(-1);
		}
	}

	if (verbose)
	{
		fprintf(stderr, "Closing\n");
	}

	if (handle_in)
	{
		snd_rawmidi_drain(handle_in);
		snd_rawmidi_close(handle_in);
	}

	if (handle_out)
	{
		snd_rawmidi_drain(handle_out);
		snd_rawmidi_close(handle_out);
	}

	if (fd_in != -1)
	{
		close(fd_in);
	}

	if (fd_out != -1)
	{
		close(fd_out);
	}

	return 0;
}
